#if USEUNITASK

using Cysharp.Threading.Tasks;
using System;
using System.Threading;
using TMPro;
using UnityEngine;
using UnityEngine.UI;


namespace MyUtility.Utility
{

    /// <summary>
    /// 轻量版DOTween
    /// 性能优化
    /// </summary>
    public static class LiteDoTween
    {
        #region LiteDoTeween

        private static async UniTask DoLoopEase(float duration, Ease ease, CancellationToken token, Action<float> evaluate)
        {
            try
            {
                var curTime = 0f;
                while (true)
                {
                    curTime += Time.unscaledDeltaTime;
                    if (curTime >= duration) break;
                    
                    var val = EasingFunction.DoEase(ease, 0, 1, curTime / duration);
                    evaluate?.Invoke(val);
                    
                    await UniTask.DelayFrame(1, PlayerLoopTiming.Update, token, cancelImmediately: true);
                }
            }
            catch { }
        }

        private static async UniTask DoLoopCurve(float duration, AnimationCurve curve, CancellationToken token, Action<float> evaluate)
        {
            try
            {
                var curTime = 0f;
                while (true)
                {
                    curTime += Time.unscaledDeltaTime;
                    if (curTime >= duration) break;

                    var val = curve.Evaluate(curTime / duration);
                    evaluate?.Invoke(val);
                    await UniTask.DelayFrame(1, PlayerLoopTiming.Update, token, cancelImmediately: true);
                }
            }
            catch { }
        }

        public static async UniTask DoLerp(float startVal, float target, Action<float> callback, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    var delta = Mathf.Lerp(startVal, target, val);
                    callback?.Invoke(delta);
                });
                callback?.Invoke(target);
            }
            catch { }
        }

        public static async UniTask DoMove(this Transform obj, Vector3 target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    obj.position = Vector3.Lerp(objPos, target, val);
                });
                obj.position = target;
            }
            catch { }
        }

        public static async UniTask DoMove(this Transform obj, Vector3 target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    obj.position = Vector3.Lerp(objPos, target, val);
                });
                obj.position = target;
            }
            catch { }
        }

        public static async UniTask DoLocalMove(this Transform obj, Vector3 localTarget, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    obj.position = Vector3.Lerp(objPos, localTarget, val);
                });
                obj.position = localTarget;
            }
            catch { }
        }
        
        public static async UniTask DoLocalMove(this Transform obj, Vector3 localTarget, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    obj.position = Vector3.Lerp(objPos, localTarget, val);
                });
                obj.position = localTarget;
            }
            catch { }
        }

        public static async UniTask DoMoveX(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objPos.x = Mathf.Lerp(objPos.x, target, val);
                    obj.position = objPos;
                });

                objPos.x = target;
                obj.position = objPos;
            }
            catch { }
        }

        public static async UniTask DoMoveX(this Transform obj, float target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    objPos.x = Mathf.Lerp(objPos.x, target, val);
                    obj.position = objPos;
                });

                objPos.x = target;
                obj.position = objPos;
            }
            catch { }
        }


        public static async UniTask DoMoveY(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objPos.y = Mathf.Lerp(objPos.y, target, val);
                    obj.position = objPos;
                });

                objPos.y = target;
                obj.position = objPos;
            }
            catch { }
        }

        public static async UniTask DoLocalMoveY(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objPos = obj.localPosition;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objPos.y = Mathf.Lerp(objPos.y, target, val);
                    obj.localPosition = objPos;
                });

                objPos.y = target;
                obj.localPosition = objPos;
            }
            catch { }
        }


        public static async UniTask DoMoveY(this Transform obj, float target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    objPos.y = Mathf.Lerp(objPos.y, target, val);
                    obj.position = objPos;
                });

                objPos.y = target;
                obj.position = objPos;
            }
            catch { }
        }


        public static async UniTask DoMoveZ(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objPos.z = Mathf.Lerp(objPos.z, target, val);
                    obj.position = objPos;
                });

                objPos.z = target;
                obj.position = objPos;
            }
            catch { }
        }


        public static async UniTask DoMoveZ(this Transform obj, float target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objPos = obj.position;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    objPos.z = Mathf.Lerp(objPos.z, target, val);
                    obj.position = objPos;
                });

                objPos.z = target;
                obj.position = objPos;
            }
            catch { }
        }

        public static async UniTask DoMove(this RectTransform rectTran, Vector2 targetPos, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objPos = rectTran.anchoredPosition;
                await DoLoopEase(duration, ease, token, val =>
                {
                    rectTran.anchoredPosition = Vector2.Lerp(objPos, targetPos, val);
                });
                rectTran.anchoredPosition = targetPos;
            }
            catch { }
        }


        public static async UniTask DoScale(this Transform obj, Vector3 target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objScl = obj.localScale;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    obj.localScale = Vector3.Lerp(objScl, target, val);
                });
                obj.localScale = target;
            }
            catch (Exception _)
            {
            }
        }


        public static async UniTask DoScale(this Transform obj, Vector3 target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objScl = obj.localScale;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    obj.localScale = Vector3.Lerp(objScl, target, val);
                });
                obj.localScale = target;
            }
            catch { }
        }


        public static async UniTask DoScaleX(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objScl = obj.localScale;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objScl.x = Mathf.Lerp(objScl.x, target, val);
                    obj.localScale = objScl;
                });

                objScl.x = target;
                obj.localScale = objScl;
            }
            catch { }
        }

        public static async UniTask DoScaleX(this Transform obj, float target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objScl = obj.localScale;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    objScl.x = Mathf.Lerp(objScl.x, target, val);
                    obj.localScale = objScl;
                });

                objScl.x = target;
                obj.localScale = objScl;
            }
            catch { }
        }

        public static async UniTask DoScaleY(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objScl = obj.localScale;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objScl.y = Mathf.Lerp(objScl.y, target, val);
                    obj.localScale = objScl;
                });

                objScl.y = target;
                obj.localScale = objScl;
            }
            catch { }
        }

        public static async UniTask DoScaleY(this Transform obj, float target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objScl = obj.localScale;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    objScl.y = Mathf.Lerp(objScl.y, target, val);
                    obj.localScale = objScl;
                });

                objScl.y = target;
                obj.localScale = objScl;
            }
            catch { }
        }

        public static async UniTask DoScaleZ(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objScl = obj.localScale;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objScl.z = Mathf.Lerp(objScl.z, target, val);
                    obj.localScale = objScl;
                });

                objScl.z = target;
                obj.localScale = objScl;
            }
            catch { }
        }

        public static async UniTask DoScaleZ(this Transform obj, float target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objScl = obj.localScale;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    objScl.z = Mathf.Lerp(objScl.z, target, val);
                    obj.localScale = objScl;
                });

                objScl.z = target;
                obj.localScale = objScl;
            }
            catch { }
        }

        public static async UniTask DoRotation(this Transform obj, Vector3 target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objRot = obj.eulerAngles;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    obj.eulerAngles = Vector3.Lerp(objRot, target, val);
                });

                obj.eulerAngles = objRot;
            }
            catch { }
        }

        public static async UniTask DoRotation(this Transform obj, Vector3 target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objRot = obj.eulerAngles;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    obj.eulerAngles = Vector3.Lerp(objRot, target, val);
                });

                obj.eulerAngles = objRot;
            }
            catch { }
        }


        public static async UniTask DoRotationX(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objRot = obj.eulerAngles;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objRot.x = Mathf.Lerp(objRot.x, target, val);
                    obj.eulerAngles = objRot;
                });

                objRot.x = target;
                obj.eulerAngles = objRot;
            }
            catch { }
        }

        public static async UniTask DoRotationX(this Transform obj, float target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objRot = obj.eulerAngles;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    objRot.x = Mathf.Lerp(objRot.x, target, val);
                    obj.eulerAngles = objRot;
                });

                objRot.x = target;
                obj.eulerAngles = objRot;
            }
            catch { }
        }

        public static async UniTask DoRotationY(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objRot = obj.eulerAngles;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objRot.y = Mathf.Lerp(objRot.y, target, val);
                    obj.eulerAngles = objRot;
                });

                objRot.y = target;
                obj.eulerAngles = objRot;
            }
            catch { }
        }


        public static async UniTask DoRotationY(this Transform obj, float target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objRot = obj.eulerAngles;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    objRot.y = Mathf.Lerp(objRot.y, target, val);
                    obj.eulerAngles = objRot;
                });

                objRot.y = target;
                obj.eulerAngles = objRot;
            }
            catch { }
        }


        public static async UniTask DoRotationZ(this Transform obj, float target, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var objRot = obj.eulerAngles;
                await DoLoopEase(duration, ease, token, (val) =>
                {
                    objRot.z = Mathf.Lerp(objRot.z, target, val);
                    obj.eulerAngles = objRot;
                });

                objRot.z = target;
                obj.eulerAngles = objRot;
            }
            catch { }
        }

        public static async UniTask DoRotationZ(this Transform obj, float target, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var objRot = obj.eulerAngles;
                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    objRot.z = Mathf.Lerp(objRot.z, target, val);
                    obj.eulerAngles = objRot;
                });

                objRot.z = target;
                obj.eulerAngles = objRot;
            }
            catch { }
        }


        public static async UniTask DoFade(this SpriteRenderer sprite, float fadeValue, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var color = sprite.color;
                var alpha = sprite.color.a;

                await DoLoopEase(duration, ease, token, (val) =>
                {
                    color.a = Mathf.Lerp(alpha, fadeValue, val);
                    sprite.color = color;
                });
                color.a = alpha;
                sprite.color = color;
            }
            catch { }
        }


        public static async UniTask DoFade(this SpriteRenderer sprite, float fadeValue, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var color = sprite.color;
                var alpha = sprite.color.a;

                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    color.a = Mathf.Lerp(alpha, fadeValue, val);
                    sprite.color = color;
                });
                color.a = alpha;
                sprite.color = color;
            }
            catch { }
        }



        public static async UniTask DoFade(this TextMeshPro text, float fadeValue, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var color = text.color;
                var alpha = text.color.a;

                await DoLoopEase(duration, ease, token, (val) =>
                {
                    color.a = Mathf.Lerp(alpha, fadeValue, val);
                    text.color = color;
                });
                color.a = alpha;
                text.color = color;
            }
            catch { }
        }


        public static async UniTask DoFade(this TextMeshPro text, float fadeValue, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var color = text.color;
                var alpha = text.color.a;

                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    color.a = Mathf.Lerp(alpha, fadeValue, val);
                    text.color = color;
                });
                color.a = alpha;
                text.color = color;
            }
            catch { }
        }

        public static async UniTask DoFade(this Graphic graphic, float fadeValue, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                var color = graphic.color;
                var alpha = graphic.color.a;

                await DoLoopCurve(duration, curve, token, (val) =>
                {
                    color.a = Mathf.Lerp(alpha, fadeValue, val);
                    graphic.color = color;
                });
                color.a = fadeValue;
                graphic.color = color;
            }
            catch { }
        }
        
        public static async UniTask DoFade(this Graphic graphic, float fadeValue, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var color = graphic.color;
                var alpha = graphic.color.a;

                await DoLoopEase(duration, ease, token, (val) =>
                {
                    color.a = Mathf.Lerp(alpha, fadeValue, val);
                    graphic.color = color;
                });
                
                color.a = fadeValue;
                graphic.color = color;
            }
            catch { }
        }



        /// <summary>
        /// 二阶贝塞尔移动
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="startPoint"></param>
        /// <param name="ctrlPoint"></param>
        /// <param name="endPoint"></param>
        /// <param name="segment"></param>
        /// <param name="duration"></param>
        /// <param name="ease"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async UniTask DoBezierMove(this Transform obj, Vector3 startPoint, Vector3 ctrlPoint, Vector3 endPoint, float duration, Ease ease, CancellationToken token)
        {
            try
            {
                var curTime = 0f;
                while (true)
                {
                    var t = curTime / duration;
                    t = Mathf.Clamp01(t);
                    var point = BezierUtils.CalculateCubicBezierPoint(t, startPoint, ctrlPoint, endPoint);
                    var easeVal = EasingFunction.DoEase(ease, 0, duration, curTime / duration);
                    obj.position = Vector3.Lerp(obj.position, point, easeVal);

                    curTime += Time.deltaTime;
                    if (curTime >= duration) break;
                    await UniTask.Yield(token);
                }
            }
            catch { }
        }


        public static async UniTask DoBezierMove(this Transform obj, Vector3 startPoint, Vector3 ctrlPoint, Vector3 endPoint, float duration, AnimationCurve curve, CancellationToken token)
        {
            try
            {
                obj.position = startPoint;
                var curTime = 0f;
                while (true)
                {
                    var t = curTime / duration;
                    t = Mathf.Clamp01(t);
                    var point = BezierUtils.CalculateCubicBezierPoint(t, startPoint, ctrlPoint, endPoint);
                    //var val = curve.Evaluate(t);
                    obj.position = Vector3.Lerp(obj.position, point, t);

                    curTime += Time.deltaTime;
                    if (curTime >= duration) break;
                    await UniTask.Yield(token);
                }
            }
            catch { }
        }

        #endregion
    }

}

#endif